package com.fzk;

import com.fzk.core.btree.BTreeFile;
import com.fzk.env.MyEnv;
import com.fzk.env.conf.DataConf;
import com.fzk.env.conf.ServerConf;
import com.fzk.ha.HAService;
import com.fzk.log.Logger;
import com.fzk.server.NettyHttpServer;
import com.fzk.utils.MyHmac;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ThreadLocalRandom;

/**
 * @author fzk
 * @datetime 2023-01-14 21:50:02
 */
public class Main {
    public static void main(String[] args) throws Exception {
        ServerConf serverConf = MyEnv.getConf(ServerConf.ConfName, ServerConf.class);
        DataConf dataConf = MyEnv.getConf(DataConf.ConfName, DataConf.class);

        NettyHttpServer srv = new NettyHttpServer(serverConf, dataConf);
        srv.start();

//        testHAStore();
//        testBTree();

        // 通过 Shutdown Hook 监听信号：SIGTERM(kill -15/默认)、SIGINT(kill -2 / ctrl+c)
        // SIGKILL信号即kill -9无法监听
        Runtime.getRuntime().addShutdownHook(new Thread(() -> {
            try {
                Logger.info("shutdown hook called, will shutdown the server");
                // 等10s强制关闭
                new Timer("forceShutdownWait10s", true).schedule(new TimerTask() {
                    @Override
                    public void run() {
                        Logger.warning("wait for netty server shutdown 10s, force shutdown due to timeout");
                        System.exit(0);// 结束虚拟机进程
                    }
                }, 10 * 1000L);
                srv.close();
                Thread.sleep(100);// 等日志异步写入
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }));
    }

    public static void testHAStore() throws IOException, CloneNotSupportedException, InterruptedException {
        ServerConf serverConf = MyEnv.getConf("server.properties", ServerConf.class);
        DataConf dataConf = MyEnv.getConf("data.properties", DataConf.class);
        NettyHttpServer master = new NettyHttpServer(serverConf, dataConf);
        new Thread(() -> {
            try {
                master.start();
            } catch (IOException e) {
                throw new RuntimeException(e);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }).start();

        ServerConf serverConfClone = serverConf.clone();
        serverConfClone.setRole(HAService.Replica);
        serverConfClone.setPort(serverConfClone.getPort() + 1);
        serverConfClone.setHaPort(serverConfClone.getHaPort() + 1);
        DataConf dataConfClone = dataConf.clone();
        dataConfClone.setDataDir("data_replica");

        NettyHttpServer replica = new NettyHttpServer(serverConfClone, dataConfClone);
        replica.start();

        Thread.sleep(1000 * 100);
    }

    static void testBTree() throws IOException, InterruptedException {
        DataConf dataConf = MyEnv.getConf("data.properties", DataConf.class);
//        MyFileUtil.deleteDir(Path.of(dataConf.getDataDir()));
        BTreeFile btree = new BTreeFile(Path.of(dataConf.getDataDir(), dataConf.getBtreeFileName()), dataConf.getBtreeCachePageCount());
        HashSet<String> keys = new HashSet<>();
        ArrayList<String> list = new ArrayList<>();
        try {
            for (int i = 0; i < 1000; i++) {
                String key = String.format("k%032d", ThreadLocalRandom.current().nextInt(0, 1000_000_000));
                key = MyHmac.md5(key.getBytes(StandardCharsets.UTF_8));
                Logger.println(String.format("即将插入%s", key));
                btree.put(key, i);
//                btree.printSelf();
                keys.add(key);
                list.add(key);
            }
            btree.printSelf();
            btree.checkTree();

            Thread.sleep(1000);

            keys.forEach((key) -> {
                try {
                    Logger.println("即将移除key: " + key);
                    if (btree.remove(key) == null) {
                        throw new RuntimeException("不存在key: " + key);
                    }
//                    btree.printSelf();
                    btree.checkTree();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            });
            btree.printSelf();
            btree.checkTree();

            Thread.sleep(1000 * 3);
        } finally {
            btree.close();
            Logger.println("插入顺序: " + list);
            Thread.sleep(500);
        }
    }
}
